package OOPAndMultiThreadArchiveManager.Server.Controllers;

import OOPAndMultiThreadArchiveManager.Server.Entities.Archive;
import OOPAndMultiThreadArchiveManager.Server.Entities.DbContext;
import OOPAndMultiThreadArchiveManager.Server.Models.ApiResponse;
import OOPAndMultiThreadArchiveManager.Server.Tools.PropertiesLoader;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.*;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Random;

public class FileUploadController extends Thread {
    private Socket socket;
    private InputStream stream;
    private static final ObjectMapper objectMapper = new ObjectMapper();
    private final DbContext dbContext = new DbContext();
    private String saveFilesPath = "";

    public FileUploadController(Socket socket) throws IOException {
        saveFilesPath = PropertiesLoader.SaveFilesPath;
        this.socket = socket;
        stream = socket.getInputStream();
    }

    private static String byteToHexLower(byte[] b) {
        var hashcode = new StringBuilder();
        var stringTemp = "";
        for (byte value : b) {
            stringTemp = Integer.toHexString(value & 0XFF);
            if (stringTemp.length() == 1) {
                hashcode.append("0").append(stringTemp);
            } else {
                hashcode.append(stringTemp);
            }
        }
        return hashcode.toString();
    }

    @Override
    public void run() {
        DataOutputStream outputStream = null;
        try {
            var tempFileName = new File(saveFilesPath + "temp" + System.currentTimeMillis() + (new Random().nextInt(9) + 1));
            if (!tempFileName.createNewFile()) {
                outputStream = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
                outputStream.writeUTF(objectMapper.writeValueAsString(ApiResponse.Failed("上传文件失败")));
                return;
            }
            var fileOutputStream = new FileOutputStream(tempFileName);

            var buffer = new byte[1024];
            var length = 0;
            var messageDigest = MessageDigest.getInstance("SHA-256");
            while ((length = stream.read(buffer)) != -1) {
                fileOutputStream.write(buffer, 0, length);
                messageDigest.update(buffer, 0, length);
            }
            fileOutputStream.flush();
            fileOutputStream.close();
            var digest = messageDigest.digest();
            var sha256 = byteToHexLower(digest);
            var archiveNameList = dbContext.select(
                    new Archive(0,sha256,null,null,null,null),
                    List.of("hashCode"));
            outputStream = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
            if (archiveNameList.isEmpty() || archiveNameList.get(0).fileName.isEmpty()) {
                outputStream.writeUTF(objectMapper.writeValueAsString(ApiResponse.Failed("上传文件失败")));
            } else {
                var fileName = saveFilesPath + archiveNameList.get(0).fileName;
                var file = new File(tempFileName.getPath());
                if (file.renameTo(new File(fileName))) {
                    outputStream.writeUTF(objectMapper.writeValueAsString(ApiResponse.Success(null)));
                } else {
                    outputStream.writeUTF(objectMapper.writeValueAsString(ApiResponse.Failed("上传文件失败")));
                }
            }
            outputStream.flush();
            outputStream.close();
            socket.close();
        } catch (IOException | NoSuchAlgorithmException e) {
            if (e instanceof IOException) {
                try {
                    outputStream = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
                    outputStream.writeUTF(objectMapper.writeValueAsString(ApiResponse.Failed("上传文件失败")));
                    outputStream.flush();
                    outputStream.close();
                    socket.close();
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            } else {
                throw new RuntimeException(e);
            }
        }
    }
}